package dataImport.action;

import java.util.HashMap;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import dataImport.action.abstracts.AbstractEntityReader;
import dataImport.action.abstracts.AbstractTempEntityReader;
import dataImport.action.helper.UnityGuardian;
import dataImport.action.manager.EntityReaderManager;
import dataImport.action.manager.storage.EntityReaderStorageKey;
import dataImport.exception.DuplicateAttributeException;
import dataImport.exception.MissingAttributeException;
import dataImport.exception.MissingReader;
import dataImport.exception.NoEntityRelatedWithException;
import dataImport.exception.UniquenessRestrictionViolatedException;
import dataImport.model.CustomAction;
import dataImport.model.Object;
import dataImport.model.Place;
import dataImport.model.PredefinedAction;
import dataImport.model.PredefinedActionTemp;
import dataImport.model.State;
import dataImport.model.StaticAction;
import dataImport.model.abstracts.AbstractEntity;


public class PredefinedActionReader extends AbstractTempEntityReader {

	private static final long serialVersionUID = 6982676265057367342L;

	private static final String ATTRIBUTE_VALUE = "value";

	private UnityGuardian commandActionIdStaticActionEntityId;

	public PredefinedActionReader() {
		super();
		this.commandActionIdStaticActionEntityId = new UnityGuardian();
	}

	@Override
	protected void checkRequiredReaders(final EntityReaderManager entityReaderManager) throws MissingReader {
		entityReaderManager.contains(EntityReaderStorageKey.OBJECT);
		entityReaderManager.contains(EntityReaderStorageKey.PLACE);
		entityReaderManager.contains(EntityReaderStorageKey.STATE);
		entityReaderManager.contains(EntityReaderStorageKey.CUSTOM_ACTION);
		entityReaderManager.getCustomActionReader().isFixed();
	}

	@Override
	protected AbstractEntity createEntity(Element element) {
		final String name = element.getNodeName();
		final String value = element.getAttribute(ATTRIBUTE_VALUE);
		final Element parentNode = (Element) element.getParentNode();
		final String parentNodeName = parentNode.getNodeName();
		final String parentNodeId = parentNode.getAttribute(AbstractEntityReader.ATTRIBUTE_ID);

		this.verifyRestrictions(parentNodeName, parentNodeId, name, value);

		return new PredefinedActionTemp(parentNodeId, StaticAction.get(name), value);
	}

	@Override
	protected void fixEntities(final HashMap<String, AbstractEntity> newEntities, final EntityReaderManager entityReaderManager) throws MissingReader {
		final ObjectReader objectReader = entityReaderManager.getObjectReader();
		final PlaceReader placeReader = entityReaderManager.getPlaceReader();
		final StateReader stateReader = entityReaderManager.getStateReader();
		final CustomActionReader customActionReader = entityReaderManager.getCustomActionReader();

		for (final AbstractEntity abstractEntity : this.abstractEntities.values()) {
			final PredefinedActionTemp predefinedActionTemp = (PredefinedActionTemp) abstractEntity;

			final String customActionId = predefinedActionTemp.getCustomActionId();
			final StaticAction staticAction = predefinedActionTemp.getStaticAction();
			final String entityId = predefinedActionTemp.getEntityId();

			final CustomAction action = (CustomAction) customActionReader.getEntityWithId(customActionId);

			switch (staticAction) {
				case CREATEOBJECT:
				case DESTROYOBJECT:
					final Object object = (Object) objectReader.getEntityWithId(entityId);
					newEntities.put(AbstractEntity.createId(customActionId, staticAction.getName(), entityId), new PredefinedAction(action, staticAction, object));
					break;
				case CREATESTATE:
				case DESTROYSTATE:
					final State state = (State) stateReader.getEntityWithId(entityId);
					newEntities.put(AbstractEntity.createId(customActionId, staticAction.getName(), entityId), new PredefinedAction(action, staticAction, state));
					break;
				case ACTIVATEACTION:
				case DEACTIVATEACTION:
					final CustomAction customAction = (CustomAction) customActionReader.getEntityWithId(entityId);
					newEntities.put(AbstractEntity.createId(customActionId, staticAction.getName(), entityId), new PredefinedAction(action, staticAction, customAction));
					break;
				case ADDPLACE:
				case REMOVEPLACE:
					final Place place = (Place) placeReader.getEntityWithId(entityId);
					newEntities.put(AbstractEntity.createId(customActionId, staticAction.getName(), entityId), new PredefinedAction(action, staticAction, place));
					break;
				default:
					break;
			}
		}
	}

	@Override
	public void read(Document document) {
		for (StaticAction staticAction : StaticAction.values()) {
			super.read(staticAction.getName(), document);
		}
	}

	private void verifyRestrictions(final String commandActionName, final String commandActionId, final String staticActionName, final String entityId) {
		if (entityId.isEmpty()) {
			throw new MissingAttributeException(ATTRIBUTE_VALUE);
		}

		try {
			this.commandActionIdStaticActionEntityId.ensureUniqueness(commandActionId, staticActionName, entityId);
		}
		catch (final UniquenessRestrictionViolatedException e) {
			throw new DuplicateAttributeException("predefinedAction", staticActionName);
		}

		if (!commandActionName.equals(CustomActionReader.ENTITY_NAME)) {
			throw new NoEntityRelatedWithException(staticActionName, CustomActionReader.ENTITY_NAME);
		}
	}
}
